#import "CQConnectionsController.h"
#import "CQChatController.h"
#import "CQConnectionTableCell.h"

#import <ChatCore/MVChatConnection.h>

@interface CQConnectionsController (CQConnectionsControllerPrivate)
- (unsigned) _connectedConnectionsCount;
- (void) _disablePowerManagment;
- (void) _enablePowerManagment;
- (void) _updateConnectTimes;
- (void) _loadConnectionList;
- (void) _saveConnectionList;
- (void) _createPreferenceCellsIfNeeded;
@end

static CQConnectionsController *sharedInstance = nil;

@implementation CQConnectionsController
+ (CQConnectionsController *) defaultController {
	return ( sharedInstance ? sharedInstance : ( sharedInstance = [[self alloc] init] ) );
}

- (id) init {
	if( ! ( self = [super init] ) )
		return nil;

	_connections = [[NSMutableArray alloc] init];
	_connectionCells = [[NSMutableArray alloc] init];

//	[self _enablePowerManagment];
	[self _loadConnectionList];

	return self;
}

- (void) dealloc {
	[_connections release];
	[_mainView release];
	[_transitionView release];
	[_navigationBar release];
	[_connectionsTable release];
	[_settingsTable release];
	[super dealloc];
}

#pragma mark -

/*
- (BOOL) respondsToSelector:(SEL) selector {
	NSLog(@"respondsToSelector: %s", selector);
	return [super respondsToSelector:selector];
}
*/

- (void) showConnections {
	if( _showingConnections )
		return;

	if( ! _mainView ) {
		CGRect screenRect = [UIHardware fullScreenApplicationContentRect];
		CGRect contentRect = screenRect;

		contentRect.origin.y = 0.;

		_mainView = [[UIView alloc] initWithFrame:contentRect];

		_navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0., 0., screenRect.size.width, 45.)];
		[_navigationBar setDelegate:self];
		[_navigationBar showLeftButton:[UIImage applicationImageNamed:@"plus.png"] withStyle:0 rightButton:@"Done" withStyle:3];

		UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:@"Connections"];
		[_navigationBar pushNavigationItem:item];
		[item release];

		contentRect.size.height -= 45.;

		_connectionsTable = [[UITableView alloc] initWithFrame:contentRect];
		[_connectionsTable setShowScrollerIndicators:YES];
		[_connectionsTable setDataSource:self];
		[_connectionsTable setDelegate:self];
		[_connectionsTable setAllowsReordering:YES];
		[_connectionsTable setSeparatorStyle:1];
		[_connectionsTable setRowHeight:48.];

		UITableColumn *column = [[UITableColumn alloc] initWithTitle:@"Connections" identifier:@"connections" width:contentRect.size.width];
		[_connectionsTable addTableColumn:column];

		_settingsTable = [[UIPreferencesTable alloc] initWithFrame:contentRect];
		[_settingsTable setDataSource:self];
		[_settingsTable setDelegate:self];

		contentRect.origin.y = 45.;

		_transitionView = [[UITransitionView alloc] initWithFrame:contentRect];
		[_transitionView setDelegate:self];
		[_transitionView addSubview:_connectionsTable];

		[_mainView addSubview:_navigationBar];
		[_mainView addSubview:_transitionView];
	}

	_showingConnections = YES;

	[_connectTimeUpdateTimer invalidate];
	[_connectTimeUpdateTimer release];
	_connectTimeUpdateTimer = [[NSTimer scheduledTimerWithTimeInterval:1. target:self selector:@selector( _updateConnectTimes ) userInfo:nil repeats:YES] retain];

	[_connectionsTable reloadData];
	[self _updateConnectTimes];

	UIWindow *window = [[CQChatController defaultController] chatsWindow];
	UIView *contentView = [window contentView];

	CGRect startFrame = [_mainView frame];
	CGRect endFrame = startFrame;

	startFrame.origin.y = startFrame.size.height;
	endFrame.origin.y = 0.;

	UIFrameAnimation *animation = [[UIFrameAnimation alloc] initWithTarget:_mainView];
	[animation setStartFrame:startFrame];
	[animation setEndFrame:endFrame];
	[animation setSignificantRectFields:2]; // the y position of the rect
	[animation setAnimationCurve:1]; // ease in

	[_mainView setFrame:startFrame];
	[contentView addSubview:_mainView];
	[[UIAnimator sharedAnimator] addAnimation:animation withDuration:0.33 start:YES];

	[animation release];
}

- (void) hideConnections {
	if( ! _showingConnections )
		return;

	[_connectTimeUpdateTimer invalidate];
	[_connectTimeUpdateTimer release];
	_connectTimeUpdateTimer = nil;

	CGRect endFrame = [_mainView frame];
	endFrame.origin.y = endFrame.size.height;

	UIFrameAnimation *animation = [[UIFrameAnimation alloc] initWithTarget:_mainView];
	[animation setStartFrame:[_mainView frame]];
	[animation setEndFrame:endFrame];
	[animation setSignificantRectFields:2]; // the y position of the rect
	[animation setAnimationCurve:2]; // ease out
	[animation setDelegate:self];

	[[UIAnimator sharedAnimator] addAnimation:animation withDuration:0.33 start:YES];

	[animation release];
}

#pragma mark -

- (void) _createPreferenceCellsIfNeeded {
	if( _autoConnectCell )
		return;

	// Create all of our cells for the first time.

	_autoConnectCell = [[UIPreferencesControlTableCell alloc] init];
	[_autoConnectCell setTitle:@"Connect on Launch"];

	UISwitchControl *switchControl = [[UISwitchControl alloc] initWithFrame:CGRectMake(200., 10., 50., 20.)];
	[_autoConnectCell setControl:switchControl];
	[switchControl release];

	_serverCell = [[UIPreferencesTextTableCell alloc] init];
	[_serverCell setTitle:@"Host Name"];
	[_serverCell setPlaceHolderValue:@"irc.example.com"];
	[[_serverCell textField] setAutoCapsType:0]; // no inital caps
	[[_serverCell textField] setAutoCorrectionType:1]; // no correction
	[[_serverCell textField] setPreferredKeyboardType:3]; // url keyboard

	_serverPortCell = [[UIPreferencesTextTableCell alloc] init];
	[_serverPortCell setTitle:@"Port"];
	[_serverPortCell setPlaceHolderValue:@"6667"];
	[[_serverPortCell textField] setAutoCorrectionType:1]; // no correction
	[[_serverPortCell textField] setPreferredKeyboardType:2]; // number keypad

	_sslCell = [[UIPreferencesControlTableCell alloc] init];
	[_sslCell setTitle:@"Uses SSL"];

	switchControl = [[UISwitchControl alloc] initWithFrame:CGRectMake(200., 10., 50., 20.)];
	[_sslCell setControl:switchControl];
	[switchControl release];

	_nicknameCell = [[UIPreferencesTextTableCell alloc] init];
	[_nicknameCell setTitle:@"Nickname"];
	[_nicknameCell setPlaceHolderValue:@"Required"];
	[[_nicknameCell textField] setAutoCapsType:0]; // no inital caps

	_nicknamePasswordCell = [[UIPreferencesTextTableCell alloc] init];
	[_nicknamePasswordCell setTitle:@"Password"];
	[_nicknamePasswordCell setPlaceHolderValue:@"Optional"];
	[[_nicknamePasswordCell textField] setSecureTextEntry:YES];
	[[_nicknamePasswordCell textField] setSecure:YES];

	_alternateNicknamesCell = [[UIPreferencesTextTableCell alloc] init];
	[_alternateNicknamesCell setTitle:@"Alternates"];
	[_alternateNicknamesCell setPlaceHolderValue:@"First Second..."];
	[[_alternateNicknamesCell textField] setAutoCapsType:0]; // no inital caps
	[[_alternateNicknamesCell textField] setAutoCorrectionType:1]; // no correction

	_usernameCell = [[UIPreferencesTextTableCell alloc] init];
	[_usernameCell setTitle:@"User Name"];
	[_usernameCell setPlaceHolderValue:@"Optional"];
	[[_usernameCell textField] setAutoCapsType:0]; // no inital caps

	_serverPasswordCell = [[UIPreferencesTextTableCell alloc] init];
	[_serverPasswordCell setTitle:@"Password"];
	[_serverPasswordCell setPlaceHolderValue:@"Optional"];
	[[_serverPasswordCell textField] setSecureTextEntry:YES];
	[[_serverPasswordCell textField] setSecure:YES];

	_realNameCell = [[UIPreferencesTextTableCell alloc] init];
	[_realNameCell setTitle:@"Real Name"];
	[_realNameCell setPlaceHolderValue:@"Optional"];

	_autoRoomsCell = [[UIPreferencesTextTableCell alloc] init];
	[_autoRoomsCell setTitle:@"Join Rooms"];
	[_autoRoomsCell setPlaceHolderValue:@"First Second..."];
	[[_autoRoomsCell textField] setAutoCapsType:0]; // no inital caps
	[[_autoRoomsCell textField] setAutoCorrectionType:1]; // no correction
}

#pragma mark -

- (void) animator:(UIAnimator *) animator stopAnimation:(UIAnimation *) animation {
	[_mainView removeFromSuperview];
	_showingConnections = NO;
}

#pragma mark -

- (int) numberOfRowsInTable:(UITableView *) table {
	return [_connections count];
}

- (UITableViewCell *) table:(UITableView *) table cellForRow:(int) row column:(int) col {
	CQConnectionTableCell *cell = [_connectionCells objectAtIndex:row];
	MVChatConnection *connection = [_connections objectAtIndex:row];

	[cell setTitle:[connection server]];
	[cell setNickname:[connection nickname]];

	if ([connection status] == MVChatConnectionDisconnectedStatus)
		[cell setStatus:CQConnectionTableCellNotConnectedStatus];
	else if ([connection status] == MVChatConnectionServerDisconnectedStatus)
		[cell setStatus:CQConnectionTableCellServerDisconnectedStatus];
	else if ([connection status] == MVChatConnectionConnectingStatus)
		[cell setStatus:CQConnectionTableCellConnectingStatus];
	else if ([connection status] == MVChatConnectionConnectedStatus)
		[cell setStatus:CQConnectionTableCellConnectedStatus];

	return (UITableViewCell *)cell;
}

- (BOOL) table:(UITableView *) table canDeleteRow:(int) row {
	return YES;
}

- (BOOL) table:(UITableView *) table canMoveRow:(int) row {
	return YES;
}

- (void) table:(UITableView *) table willSwipeToDeleteRow:(int) row {
	
}

- (void) table:(UITableView *) table willCancelSwipeToDeleteRow:(int) row {
	CQConnectionTableCell *cell = [_connectionCells objectAtIndex:row];
	[cell setShowDisclosure:YES animated:YES];
}

- (void) table:(UITableView *) table deleteRow:(int) row {
	MVChatConnection *connection = [_connections objectAtIndex:row];
	if( ! connection ) return;

	[self removeConnection:connection];
}

- (void) table:(UITableView *) table movedRow:(int) oldRow toRow:(int) newRow {
	
}

- (int) table:(UITableView *) table moveDestinationForRow:(int)row withSuggestedDestinationRow:(int) suggestedRow {
	return suggestedRow;
}

- (BOOL) table:(UITableView *) table canSelectRow:(int) row {
	return YES;
}

- (BOOL) table:(UITableView *) table disclosureClickableForRow:(int) row {
	return YES;
}

- (void) table:(UITableView *) table disclosureClickedForRow:(int) row {
	[_editingConnection release];
	_editingConnection = [[_connections objectAtIndex:row] retain];

	UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Edit"];
	[_navigationBar pushNavigationItem:navigationItem];
	[navigationItem release];

	[self _createPreferenceCellsIfNeeded];

	NSDictionary *extraInfo = [[_editingConnection persistentInformation] objectForKey:@"CQConnectionsControllerExtraInfo"];

	NSNumber *automatic = [extraInfo objectForKey:@"automatic"];
	[(UISwitchControl *)[_autoConnectCell control] setValue:( [automatic boolValue] ? 1. : 0. )];

	[_serverCell setValue:[_editingConnection server]];
	[_serverPortCell setValue:[NSString stringWithFormat:@"%hu", [_editingConnection serverPort]]];
	[(UISwitchControl *)[_sslCell control] setValue:( [_editingConnection isSecure] ? 1. : 0. )];
	[_nicknameCell setValue:[_editingConnection preferredNickname]];
	[_nicknamePasswordCell setValue:[_editingConnection nicknamePassword]];
	[_alternateNicknamesCell setValue:[[_editingConnection alternateNicknames] componentsJoinedByString:@" "]];
	[_usernameCell setValue:[_editingConnection username]];
	[_serverPasswordCell setValue:[_editingConnection password]];
	[_realNameCell setValue:[_editingConnection realName]];

	NSArray *autoRooms = [extraInfo objectForKey:@"rooms"];
	[_autoRoomsCell setValue:[autoRooms componentsJoinedByString:@" "]];

	[_settingsTable setKeyboardVisible:NO animated:NO];
	[_settingsTable setBottomBufferHeight:5.];
	[_settingsTable reloadData];		

	[_connectionsTable highlightRow:row];
	[_navigationBar showLeftButton:nil withStyle:0 rightButton:@"Delete" withStyle:1];
	[_transitionView transition:1 toView:_settingsTable];
}

- (void) tableRowSelected:(NSNotification *) notification {
	if( _editingConnection )
		return;

	int index = [_connectionsTable selectedRow];
	if( index < 0 || index >= [_connections count] )
		return;

	MVChatConnection *connection = [_connections objectAtIndex:index];

	if( [connection status] == MVChatConnectionConnectedStatus || [connection status] == MVChatConnectionConnectingStatus ) {
		UIAlertSheet *sheet = [[UIAlertSheet alloc] init];
		[sheet setDelegate:self];
		[sheet setBlocksInteraction:YES];
		[sheet setDimsBackground:YES];

		[sheet setDestructiveButton:[sheet addButtonWithTitle:@"Disconnect"]];
		[sheet addButtonWithTitle:@"Cancel"];

		[sheet setContext:@"disconnect"];
		[sheet presentSheetInView:_mainView];
		[sheet release];
	} else {
		if( ! [[NetworkController sharedInstance] isFatPipe] ) {
			UIAlertSheet *sheet = [[UIAlertSheet alloc] init];
			[sheet setDelegate:self];
			[sheet setBodyText:[NSString stringWithFormat:@"Are you sure you want to connect to \"%@\" over EDGE?", [connection server]]];
			[sheet setBlocksInteraction:YES];
			[sheet setDimsBackground:YES];

			[sheet setDefaultButton:[sheet addButtonWithTitle:@"Connect"]];
			[sheet addButtonWithTitle:@"Cancel"];

			[sheet setContext:@"connect"];
			[sheet presentSheetFromButtonBar:[[CQChatController defaultController] buttonBar]];
			[sheet release];
		} else {
			[connection connect];
			[_connectionsTable selectRow:-1 byExtendingSelection:NO withFade:YES scrollingToVisible:NO];
		}
	}
}

#pragma mark -

- (void) alertSheet:(UIAlertSheet *) sheet buttonClicked:(int) button {
	[sheet dismiss];

	MVChatConnection *connection = nil;
	int index = [_connectionsTable selectedRow];
	if( index >= 0 && index < [_connections count] )
		connection = [_connections objectAtIndex:index];

	if( [[sheet context] isEqual:@"connect"] ) {
		if( button == 1 && connection )
			[connection connect];
		[_connectionsTable selectRow:-1 byExtendingSelection:NO withFade:YES scrollingToVisible:NO];
	} else if( [[sheet context] isEqual:@"disconnect"] ) {
		if( button == 1 && connection )
			[connection disconnect];
		[_connectionsTable selectRow:-1 byExtendingSelection:NO withFade:YES scrollingToVisible:NO];
	} else if( [[sheet context] isEqual:@"delete"] ) {
		if( button == 1 ) {
			[_editingConnection disconnect];
			[self removeConnection:_editingConnection];

			[_editingConnection release];
			_editingConnection = nil;

			[_navigationBar popNavigationItem];
		}
	}
}

#pragma mark -

- (void) navigationBar:(UINavigationBar *) bar poppedItem:(UINavigationItem *) item {
	[_settingsTable setKeyboardVisible:NO animated:NO];
	[_settingsTable setBottomBufferHeight:5.];

	[_connectionsTable selectRow:-1 byExtendingSelection:NO withFade:NO scrollingToVisible:NO];
	[_navigationBar showLeftButton:[UIImage applicationImageNamed:@"plus.png"] withStyle:0 rightButton:@"Done" withStyle:3];
	[_transitionView transition:2 toView:_connectionsTable];

	if( _newConnection )
		_editingConnection = [[MVChatConnection alloc] initWithType:MVChatConnectionIRCType];

	if( _editingConnection ) {
		NSMutableDictionary *persistentInformation = [[_editingConnection persistentInformation] mutableCopy];
		NSMutableDictionary *extraInfo = [[persistentInformation objectForKey:@"CQConnectionsControllerExtraInfo"] mutableCopy];
		if( ! extraInfo ) extraInfo = [[NSMutableDictionary alloc] init];

		NSNumber *automatic = [NSNumber numberWithBool:( [(UISwitchControl *)[_autoConnectCell control] value] > 0. )];
		[extraInfo setObject:automatic forKey:@"automatic"];

		if( [[_serverCell value] length] )
			[_editingConnection setServer:[_serverCell value]];

		[_editingConnection setServerPort:[[_serverPortCell value] intValue]];
		[_editingConnection setSecure:( [(UISwitchControl *)[_sslCell control] value] > 0. )];

		if( [[_nicknameCell value] length] )
			[_editingConnection setNickname:[_nicknameCell value]];

		if( [[_alternateNicknamesCell value] length] )
			[_editingConnection setAlternateNicknames:[[_alternateNicknamesCell value] componentsSeparatedByString:@" "]];
		else [_editingConnection setAlternateNicknames:[NSArray array]];

		if( [[_usernameCell value] length] )
			[_editingConnection setUsername:[_usernameCell value]];

		[_editingConnection setNicknamePassword:[[_nicknamePasswordCell value] length] ? [_nicknamePasswordCell value] : nil];
		[_editingConnection setPassword:[[_serverPasswordCell value] length] ? [_serverPasswordCell value] : nil];
		[_editingConnection setRealName:[_realNameCell value] ? [_realNameCell value] : @""];

		if( [[_editingConnection nicknamePassword] length] )
			[PSKeychainUtilities setPassword:[_editingConnection nicknamePassword] forHost:[_editingConnection server] username:[_editingConnection preferredNickname] port:0 protocol:[_editingConnection urlScheme]];
		else [PSKeychainUtilities removePasswordForHost:[_editingConnection server] username:[_editingConnection preferredNickname] port:0 protocol:[_editingConnection urlScheme]];

		if( [[_editingConnection password] length] )
			[PSKeychainUtilities setPassword:[_editingConnection password] forHost:[_editingConnection server] username:[_editingConnection username] port:[_editingConnection serverPort] protocol:[_editingConnection urlScheme]];
		else [PSKeychainUtilities removePasswordForHost:[_editingConnection server] username:[_editingConnection username] port:[_editingConnection serverPort] protocol:[_editingConnection urlScheme]];

		NSArray *autoRooms = [[_autoRoomsCell value] componentsSeparatedByString:@" "];
		if( autoRooms ) [extraInfo setValue:autoRooms forKey:@"rooms"];
		else [extraInfo removeObjectForKey:@"rooms"];

		[persistentInformation setObject:extraInfo forKey:@"CQConnectionsControllerExtraInfo"];
		[extraInfo release];

		[_editingConnection setPersistentInformation:persistentInformation];
		[persistentInformation release];

		if( _newConnection && [[_serverCell value] length] && [[_nicknameCell value] length] ) {
			[self addConnection:_editingConnection];
		} else {
			[self _saveConnectionList];

			unsigned index = [_connections indexOfObjectIdenticalTo:_editingConnection];
			if( index != NSNotFound )
				[_connectionsTable reloadCellAtRow:index column:0 animated:NO];
			else [_connectionsTable reloadData];
		}

		[_editingConnection release];
		_editingConnection = nil;
	}

	_newConnection = NO;
}

- (void) navigationBar:(UINavigationBar *) bar buttonClicked:(int) button {
	if( button == 0 ) {
		if( _editingConnection ) {
			UIAlertSheet *sheet = [[UIAlertSheet alloc] init];
			[sheet setDelegate:self];
			[sheet setBlocksInteraction:YES];
			[sheet setDimsBackground:YES];

			[sheet setDestructiveButton:[sheet addButtonWithTitle:@"Delete Connection"]];
			[sheet addButtonWithTitle:@"Cancel"];

			[sheet setContext:@"delete"];
			[sheet presentSheetInView:_mainView];
			[sheet release];
		} else {
			[self hideConnections];
		}
	} else if( button == 1 ) {
		_newConnection = YES;

		UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"New"];
		[_navigationBar pushNavigationItem:navigationItem];
		[navigationItem release];

		[(UISwitchControl *)[_autoConnectCell control] setValue:1.];
		[_serverCell setValue:@""];
		[_serverPortCell setValue:@""];
		[(UISwitchControl *)[_sslCell control] setValue:0.];
		[_nicknameCell setValue:@""];
		[_nicknamePasswordCell setValue:@""];
		[_alternateNicknamesCell setValue:@""];
		[_usernameCell setValue:@""];
		[_serverPasswordCell setValue:@""];
		[_realNameCell setValue:@""];
		[_autoRoomsCell setValue:@""];

		[_settingsTable setKeyboardVisible:NO animated:NO];
		[_settingsTable setBottomBufferHeight:5.];
		[_settingsTable reloadData];		

		[_navigationBar showLeftButton:nil withStyle:0 rightButton:nil withStyle:0];
		[_transitionView transition:1 toView:_settingsTable];
	}
}

#pragma mark -

- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *) table {
	return 5;
}

- (int) preferencesTable:(UIPreferencesTable *) table numberOfRowsInGroup:(int) group {
	if( group == 0 )
		return 3;
	if( group == 1 )
		return 4;
	if( group == 2 )
		return 1;
	if( group == 3 )
		return 2;
	if( group == 4 )
		return 2;
	return 0;
}

- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *) table cellForRow:(int) row inGroup:(int) group {
	[self _createPreferenceCellsIfNeeded];

	if( group == 0 ) {
		switch( row ) {
			case 0: return _serverCell;
			case 1: return _serverPortCell;
			case 2: return _sslCell;
			default: return nil;
		}
	}

	if( group == 1 ) {
		switch( row ) {
			case 0: return _realNameCell;
			case 1: return _nicknameCell;
			case 2: return _nicknamePasswordCell;
			case 3: return _alternateNicknamesCell;
			default: return nil;
		}
	}

	if( group == 2 ) {
		UIPreferencesTableCell *cell = [[UIPreferencesTableCell alloc] init];
		[cell setTitle:@"The nickname password is used to authenicate with NickServ."];
		return [cell autorelease];
	}

	if( group == 3 ) {
		switch( row ) {
			case 0: return _usernameCell;
			case 1: return _serverPasswordCell;
			default: return nil;
		}
	}

	if( group == 4 ) {
		switch( row ) {
			case 0: return _autoConnectCell;
			case 1: return _autoRoomsCell;
			default: return nil;
		}
	}

	return nil;  
}

- (float) preferencesTable:(UIPreferencesTable *) table heightForRow:(int) row inGroup:(int) group withProposedHeight:(float) proposed {
	 return proposed;
}

- (NSString *) preferencesTable:(UIPreferencesTable *) table titleForGroup:(int) group {
	if( group == 1 )
		return @"Identity";
	if( group == 3 )
		return @"Authentication";
	if( group == 4 )
		return @"Automatic";
	return nil;
}

- (BOOL) preferencesTable:(UIPreferencesTable *) table isLabelGroup:(int) group
{
	if( group == 2 )
		return YES;
	return NO;
}

#pragma mark -

- (void) _registerNotificationsForConnection:(MVChatConnection *) connection {
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshConnection: ) name:MVChatConnectionNicknameAcceptedNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _willConnect: ) name:MVChatConnectionWillConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didConnect: ) name:MVChatConnectionDidConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didNotConnect: ) name:MVChatConnectionDidNotConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didDisconnect: ) name:MVChatConnectionDidDisconnectNotification object:connection];

/*
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refresh: ) name:MVChatConnectionWillConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refresh: ) name:MVChatConnectionDidConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refresh: ) name:MVChatConnectionDidNotConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refresh: ) name:MVChatConnectionDidDisconnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refresh: ) name:MVChatConnectionNicknameAcceptedNotification object:connection];

	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _errorOccurred : ) name:MVChatConnectionErrorNotification object:connection];

	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _requestPassword: ) name:MVChatConnectionNeedNicknamePasswordNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _requestCertificatePassword: ) name:MVChatConnectionNeedCertificatePasswordNotification object:connection];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _requestPublicKeyVerification: ) name:MVChatConnectionNeedPublicKeyVerificationNotification object:connection];
*/
}

- (void) _deregisterNotificationsForConnection:(MVChatConnection *) connection {
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionNicknameAcceptedNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionWillConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionDidConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionDidNotConnectNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionDidDisconnectNotification object:connection];

/*
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionErrorNotification object:connection];

	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionNeedNicknamePasswordNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionNeedCertificatePasswordNotification object:connection];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:MVChatConnectionNeedPublicKeyVerificationNotification object:connection];
*/
}

- (void) _loadConnectionList {
	if( [_connections count] )
		return; // already loaded connections

	NSArray *list = [[NSUserDefaults standardUserDefaults] arrayForKey:@"MVChatBookmarks"];
	NSEnumerator *enumerator = [list objectEnumerator];
	NSMutableDictionary *info = nil;

	while( ( info = [enumerator nextObject] ) ) {
		MVChatConnectionType type = MVChatConnectionIRCType;
		if( [[info objectForKey:@"type"] isEqualToString:@"icb"] )
			type = MVChatConnectionICBType;
		else if( [[info objectForKey:@"type"] isEqualToString:@"irc"] )
			type = MVChatConnectionIRCType;
		else if( [[info objectForKey:@"type"] isEqualToString:@"silc"] )
			type = MVChatConnectionSILCType;
		else if( [[info objectForKey:@"type"] isEqualToString:@"xmpp"] )
			type = MVChatConnectionXMPPType;

		MVChatConnection *connection = nil;
		if( [info objectForKey:@"url"] )
			connection = [[MVChatConnection alloc] initWithURL:[NSURL URLWithString:[info objectForKey:@"url"]]];
		else connection = [[MVChatConnection alloc] initWithServer:[info objectForKey:@"server"] type:type port:[[info objectForKey:@"port"] unsignedShortValue] user:[info objectForKey:@"nickname"]];

		if( ! connection ) continue;

		NSMutableDictionary *persistentInformation = [[NSMutableDictionary alloc] init];
		if( [[info objectForKey:@"persistentInformation"] count] )
			[persistentInformation addEntriesFromDictionary:[info objectForKey:@"persistentInformation"]];

		NSMutableDictionary *extraInfo = [[NSMutableDictionary alloc] init];
		if( [info objectForKey:@"automatic"] )
			[extraInfo setObject:[info objectForKey:@"automatic"] forKey:@"automatic"];
		if( [info objectForKey:@"rooms"] )
			[extraInfo setObject:[info objectForKey:@"rooms"] forKey:@"rooms"];
		[persistentInformation setObject:extraInfo forKey:@"CQConnectionsControllerExtraInfo"];
		[extraInfo release];

		[connection setPersistentInformation:persistentInformation];
		[persistentInformation release];

		[connection setProxyType:[[info objectForKey:@"proxy"] unsignedLongValue]];
		[connection setSecure:[[info objectForKey:@"secure"] boolValue]];

		if( [[info objectForKey:@"encoding"] longValue] )
			[connection setEncoding:[[info objectForKey:@"encoding"] longValue]];
		else [connection setEncoding:[[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatEncoding"]];

		if( [info objectForKey:@"realName"] ) [connection setRealName:[info objectForKey:@"realName"]];
		if( [info objectForKey:@"nickname"] ) [connection setNickname:[info objectForKey:@"nickname"]];
		if( [info objectForKey:@"username"] ) [connection setUsername:[info objectForKey:@"username"]];
		if( [info objectForKey:@"alternateNicknames"] )
			[connection setAlternateNicknames:[info objectForKey:@"alternateNicknames"]];

		NSString *password = nil;
		if( ( password = [info objectForKey:@"nicknamePassword"] ) )
			[PSKeychainUtilities setPassword:password forHost:[connection server] username:[connection preferredNickname] port:0 protocol:[connection urlScheme]];

		if( ( password = [info objectForKey:@"password"] ) )
			[PSKeychainUtilities setPassword:password forHost:[connection server] username:[connection username] port:[connection serverPort] protocol:[connection urlScheme]];

		if( ( password = [PSKeychainUtilities passwordForHost:[connection server] username:[connection preferredNickname] port:0 protocol:[connection urlScheme]] ) && [password length] )
			[connection setNicknamePassword:password];

		if( ( password = [PSKeychainUtilities passwordForHost:[connection server] username:[connection username] port:[connection serverPort] protocol:[connection urlScheme]] ) && [password length] )
			[connection setPassword:password];

		[_connections addObject:connection];

		CQConnectionTableCell *cell = [[CQConnectionTableCell alloc] init];
		[_connectionCells addObject:cell];
		[cell release];

		[self _registerNotificationsForConnection:connection];

		if( [[info objectForKey:@"automatic"] boolValue] )
			[connection connect];

		[connection release];
	}

	[_connectionsTable reloadData];
}

- (void) _saveConnectionList {
	if( ! [_connections count] )
		return; // we have nothing to save

	NSMutableArray *saveList = [[NSMutableArray alloc] initWithCapacity:[_connections count]];
	NSEnumerator *enumerator = [_connections objectEnumerator];
	MVChatConnection *connection = nil;

	while( ( connection = [enumerator nextObject] ) ) {
		NSMutableDictionary *info = [NSMutableDictionary dictionary];

		NSDictionary *extraInfo = [[connection persistentInformation] objectForKey:@"CQConnectionsControllerExtraInfo"];
		[info addEntriesFromDictionary:extraInfo];

		[info setObject:[connection server] forKey:@"server"];
		[info setObject:[connection urlScheme] forKey:@"type"];
		[info setObject:[NSNumber numberWithBool:[connection isSecure]] forKey:@"secure"];
		[info setObject:[NSNumber numberWithLong:[connection proxyType]] forKey:@"proxy"];
		[info setObject:[NSNumber numberWithLong:[connection encoding]] forKey:@"encoding"];
		[info setObject:[NSNumber numberWithUnsignedShort:[connection serverPort]] forKey:@"port"];
		if( [connection realName] ) [info setObject:[connection realName] forKey:@"realName"];
		if( [connection username] ) [info setObject:[connection username] forKey:@"username"];
		if( [connection preferredNickname] ) [info setObject:[connection preferredNickname] forKey:@"nickname"];
		if( [[connection alternateNicknames] count] )
			[info setObject:[connection alternateNicknames] forKey:@"alternateNicknames"];

		if( [[connection persistentInformation] count] ) {
			NSMutableDictionary *persistentInformation = [[connection persistentInformation] mutableCopy];
			[persistentInformation removeObjectForKey:@"CQConnectionsControllerExtraInfo"];
			if( [persistentInformation count] )
				[info setObject:persistentInformation forKey:@"persistentInformation"];
			[persistentInformation release];
		}

		[saveList addObject:info];
	}

	[[NSUserDefaults standardUserDefaults] setObject:saveList forKey:@"MVChatBookmarks"];
	[[NSUserDefaults standardUserDefaults] synchronize];

	[saveList release];
}

#pragma mark -

- (NSArray *) allConnections {
	return _connections;
}

- (NSArray *) connectedConnections {
	NSMutableArray *ret = [NSMutableArray arrayWithCapacity:[_connections count]];
	NSEnumerator *enumerator = [_connections objectEnumerator];
	MVChatConnection *connection = nil;

	while( ( connection = [enumerator nextObject] ) )
		if( [connection isConnected] )
			[ret addObject:connection];

	return ret;
}

- (MVChatConnection *) connectionForServerAddress:(NSString *) address {
	NSArray *connections = [self connectionsForServerAddress:address];
	if( [connections count] )
		return [connections objectAtIndex:0];
	return nil;
}

- (NSArray *) connectionsForServerAddress:(NSString *) address {
	NSMutableArray *ret = [NSMutableArray arrayWithCapacity:[_connections count]];
	NSEnumerator *enumerator = [_connections objectEnumerator];
	MVChatConnection *connection = nil;

	address = [address stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@". \t\n"]];

	while( ( connection = [enumerator nextObject] ) ) {
		NSString *server = [connection server];
		NSRange range = [server rangeOfString:address options:( NSCaseInsensitiveSearch | NSLiteralSearch | NSBackwardsSearch | NSAnchoredSearch ) range:NSMakeRange( 0, [server length] )];
		if( range.location != NSNotFound && ( range.location == 0 || [server characterAtIndex:( range.location - 1 )] == '.' ) )
			[ret addObject:connection];
	}

	return ret;
}

- (BOOL) managesConnection:(MVChatConnection *) connection {
	return [_connections containsObject:connection];
}

#pragma mark -

- (void) addConnection:(MVChatConnection *) connection {
	[self insertConnection:connection atIndex:[_connections count]];
}

- (void) insertConnection:(MVChatConnection *) connection atIndex:(unsigned) index {
    if( ! connection ) return;

	[_connections insertObject:connection atIndex:index];

	CQConnectionTableCell *cell = [[CQConnectionTableCell alloc] init];
	[_connectionCells insertObject:cell atIndex:index];
	[cell release];

	[self _registerNotificationsForConnection:connection];
	[self _saveConnectionList];

	[_connectionsTable reloadData];
}

- (void) removeConnection:(MVChatConnection *) connection {
	unsigned index = [_connections indexOfObjectIdenticalTo:connection];
	[self removeConnectionAtIndex:index];
}

- (void) removeConnectionAtIndex:(unsigned) index {
	MVChatConnection *connection = [[_connections objectAtIndex:index] retain];
    if( ! connection ) return;

	[connection disconnectWithReason:nil];
	[self _deregisterNotificationsForConnection:connection];

	[connection release];

	[_connections removeObjectAtIndex:index];
	[_connectionCells removeObjectAtIndex:index];

	[self _saveConnectionList];

	[_connectionsTable reloadData];
}

- (void) replaceConnectionAtIndex:(unsigned) index withConnection:(MVChatConnection *) connection {
    if( ! connection ) return;

	MVChatConnection *oldConnection = [[_connections objectAtIndex:index] retain];
    if( ! oldConnection ) return;

	[oldConnection disconnectWithReason:nil];
	[self _deregisterNotificationsForConnection:oldConnection];

	[oldConnection release];

	[_connections replaceObjectAtIndex:index withObject:connection];

	[self _registerNotificationsForConnection:connection];
	[self _saveConnectionList];

	if( _showingConnections )
		[_connectionsTable reloadCellAtRow:index column:0 animated:NO];
}

#pragma mark -

- (unsigned) _connectedConnectionsCount {
	NSEnumerator *enumerator = [_connections objectEnumerator];
	MVChatConnection *connection = nil;
	unsigned count = 0;

	while( ( connection = [enumerator nextObject] ) )
		if( [connection isConnected] )
			++count;

	return count;
}

- (void) _disablePowerManagment {
	CFStringRef springboardBundleId = CFSTR("com.apple.springboard");
	CFPreferencesSetValue(CFSTR("SBDisablePowerManagement"), kCFBooleanTrue, springboardBundleId, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
	CFPreferencesSynchronize(springboardBundleId, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
}

- (void) _enablePowerManagment {
	CFStringRef springboardBundleId = CFSTR("com.apple.springboard");
	CFPreferencesSetValue(CFSTR("SBDisablePowerManagement"), NULL, springboardBundleId, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
	CFPreferencesSynchronize(springboardBundleId, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
}

- (void) _updateConnectTimes {
	unsigned count = [_connections count];
	if( ! count ) return;

	for( unsigned i = 0; i < count; ++i ) {
		CQConnectionTableCell *cell = [_connectionCells objectAtIndex:i];
		MVChatConnection *connection = [_connections objectAtIndex:i];
		NSDictionary *extraInfo = [[connection persistentInformation] objectForKey:@"CQConnectionsControllerExtraInfo"];
		NSDate *connectDate = [extraInfo objectForKey:@"connectDate"];

		if( connectDate && [connection isConnected] ) {
			NSTimeInterval interval = ABS( [connectDate timeIntervalSinceNow] );
			unsigned seconds = ((unsigned)interval % 60);
			unsigned minutes = ((unsigned)(interval / 60) % 60);
			unsigned hours = (interval / 3600);

			if( hours > 0 )
				[cell setTime:[NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, seconds]];
			else [cell setTime:[NSString stringWithFormat:@"%d:%02d", minutes, seconds]];
		} else if( ! [[cell time] isEqualToString:@""] ) {
			[cell setTime:@""];
		}
	}
}

- (void) _refreshConnection:(NSNotification *) notification {
	if( ! _showingConnections ) return;

	MVChatConnection *connection = [notification object];
	unsigned index = [_connections indexOfObjectIdenticalTo:connection];
	if( index != NSNotFound )
		[_connectionsTable reloadCellAtRow:index column:0 animated:NO];
}

- (void) _willConnect:(NSNotification *) notification {
	[self _refreshConnection:notification];

//	if( [[NetworkController sharedInstance] isFatPipe] )
//		[self _disablePowerManagment]; // disable power managment to keep WiFi alive

	MVChatConnection *connection = [notification object];
	NSDictionary *extraInfo = [[connection persistentInformation] objectForKey:@"CQConnectionsControllerExtraInfo"];
	NSArray *rooms = [extraInfo objectForKey:@"rooms"];
	if( [rooms count] )
		[connection joinChatRoomsNamed:rooms];
}

- (void) _didConnect:(NSNotification *) notification {
	MVChatConnection *connection = [notification object];
	NSMutableDictionary *persistentInformation = [[connection persistentInformation] mutableCopy];
	NSMutableDictionary *extraInfo = [[persistentInformation objectForKey:@"CQConnectionsControllerExtraInfo"] mutableCopy];
	if( ! extraInfo ) extraInfo = [[NSMutableDictionary alloc] init];

	[extraInfo setObject:[NSDate date] forKey:@"connectDate"];

	[persistentInformation setObject:extraInfo forKey:@"CQConnectionsControllerExtraInfo"];
	[extraInfo release];

	[connection setPersistentInformation:persistentInformation];
	[persistentInformation release];

	[self _refreshConnection:notification];
	[self _updateConnectTimes];
}

- (void) _didNotConnect:(NSNotification *) notification {
	[self _refreshConnection:notification];
}

- (void) _didDisconnect:(NSNotification *) notification {
	MVChatConnection *connection = [notification object];
	NSMutableDictionary *persistentInformation = [[connection persistentInformation] mutableCopy];
	NSMutableDictionary *extraInfo = [[persistentInformation objectForKey:@"CQConnectionsControllerExtraInfo"] mutableCopy];

	if( extraInfo ) {
		[extraInfo removeObjectForKey:@"connectDate"];

		[persistentInformation setObject:extraInfo forKey:@"CQConnectionsControllerExtraInfo"];
		[extraInfo release];

		[connection setPersistentInformation:persistentInformation];
	}

	[persistentInformation release];

	[self _refreshConnection:notification];
	[self _updateConnectTimes];

//	if( ! [self _connectedConnectionsCount] )
//		[self _enablePowerManagment]; // enable the power management now that we are disconnected
}
@end
